package cn.iocoder.yudao.framework.datasource.config;

import cn.iocoder.yudao.framework.aop.DynamicDataSourceAdvisor;
import cn.iocoder.yudao.framework.datasource.processor.DsPackageProcessor;
import cn.iocoder.yudao.framework.datasource.processor.DsRestControllerProcessor;
import cn.iocoder.yudao.framework.datasource.provider.DynamicDataSourceProvider;
import cn.iocoder.yudao.framework.datasource.provider.JdbcDynamicDataSourceProvider;
import cn.iocoder.yudao.framework.datasource.provider.YmlDynamicDataSourceProvider;
import cn.iocoder.yudao.framework.msdb.DefaultRoutingDataSource;
import cn.iocoder.yudao.framework.msdb.ICustomizeRoutingDataSource;
import cn.iocoder.yudao.framework.mybatis.interceptor.MsdbInterceptor;
import com.baomidou.dynamic.datasource.annotation.DS;
import com.baomidou.dynamic.datasource.annotation.DSTransactional;
import com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationAdvisor;
import com.baomidou.dynamic.datasource.aop.DynamicDataSourceAnnotationInterceptor;
import com.baomidou.dynamic.datasource.aop.DynamicLocalTransactionInterceptor;
import com.baomidou.dynamic.datasource.event.DataSourceInitEvent;
import com.baomidou.dynamic.datasource.event.EncDataSourceInitEvent;
import com.baomidou.dynamic.datasource.processor.DsProcessor;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceCreatorAutoConfiguration;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourceProperties;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.DynamicDataSourcePropertiesCustomizer;
import com.baomidou.dynamic.datasource.spring.boot.autoconfigure.druid.DruidDynamicDataSourceConfiguration;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.aop.Advisor;
import org.springframework.beans.factory.BeanFactory;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.ObjectProvider;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration;
import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.Role;
import org.springframework.core.annotation.Order;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.RestController;

import javax.sql.DataSource;
import java.util.Iterator;
import java.util.List;

/**
 * TODO
 *
 * @className: DynamicDataSourceAutoConfiguration
 * @author: tangkh
 * @date: 2023-09-19 17:45
 */
@Configuration
@EnableConfigurationProperties({DynamicDataSourceProperties.class})
@AutoConfigureBefore(
        value = {DataSourceAutoConfiguration.class},
        name = {"com.alibaba.druid.spring.boot.autoconfigure.DruidDataSourceAutoConfigure"}
)
@Import({DruidDynamicDataSourceConfiguration.class, DynamicDataSourceCreatorAutoConfiguration.class})
@ConditionalOnProperty(
        prefix = "spring.datasource.dynamic",
        name = {"enabled"},
        havingValue = "true",
        matchIfMissing = false
)
public class DynamicDataSourceAutoConfiguration implements InitializingBean {
    private static final Logger log = LoggerFactory.getLogger(DynamicDataSourceAutoConfiguration.class);
    private final DynamicDataSourceProperties properties;
    @Value("${spring.datasource.dynamic.extends.configType:YAML}")
    private String configType;
    @Value("${spring.datasource.dynamic.extends.dsProcessor:}")
    private String dsProcessor;
    @Value("${spring.datasource.dynamic.extends.url:}")
    private String url;
    @Value("${spring.datasource.dynamic.extends.username:}")
    private String username;
    @Value("${spring.datasource.dynamic.extends.password:}")
    private String password;
    @Value("${spring.datasource.dynamic.extends.driverClassName:}")
    private String driverClassName;
    @Value("${spring.datasource.dynamic.extends.sqlwhere:}")
    private String sqlwhere;
    public static final String SQL = "select id,url,user_name ,\"password\" , org_id,\ncase when org_id != 0 then \norg_id || '_' ||  replace(replace(replace(alias,org_id||'_',''),'iho_',''),'cloud_health_','') || \n(case when type=2 then '_slave' else '' end ) \nelse alias end\nas alias  from devops_data_source dds where alias is not null and delete_flag =false and url is not null   ";
    private final List<DynamicDataSourcePropertiesCustomizer> dataSourcePropertiesCustomizers;

    public DynamicDataSourceAutoConfiguration(DynamicDataSourceProperties properties, ObjectProvider<List<DynamicDataSourcePropertiesCustomizer>> dataSourcePropertiesCustomizers) {
        this.properties = properties;
        this.dataSourcePropertiesCustomizers = dataSourcePropertiesCustomizers.getIfAvailable();
    }

    @Bean
    @ConditionalOnMissingBean
    public DynamicDataSourceProvider dynamicDataSourceProvider() {
        return (DynamicDataSourceProvider) (this.configType.equals("YAML") ? new YmlDynamicDataSourceProvider(this.properties.getDatasource()) : new JdbcDynamicDataSourceProvider("select id,url,user_name ,\"password\" , org_id,\ncase when org_id != 0 then \norg_id || '_' ||  replace(replace(replace(alias,org_id||'_',''),'iho_',''),'cloud_health_','') || \n(case when type=2 then '_slave' else '' end ) \nelse alias end\nas alias  from devops_data_source dds where alias is not null and delete_flag =false and url is not null   " + this.sqlwhere, this.driverClassName, this.url, this.username, this.password));
    }

    @Bean
    @ConditionalOnMissingBean
    public DataSource dataSource() {
        DynamicRoutingDataSource dataSource = new DynamicRoutingDataSource();
        dataSource.setPrimary(this.properties.getPrimary());
        dataSource.setStrict(this.properties.getStrict());
        dataSource.setStrategy(this.properties.getStrategy());
        dataSource.setP6spy(this.properties.getP6spy());
        dataSource.setSeata(this.properties.getSeata());
        return dataSource;
    }

    @Role(2)
    @Bean
    @ConditionalOnProperty(
            prefix = "spring.datasource.dynamic.aop",
            name = {"enabled"},
            havingValue = "true",
            matchIfMissing = true
    )
    public Advisor dynamicDatasourceAnnotationAdvisor(DsProcessor dsProcessor) {
        DynamicDataSourceAnnotationInterceptor interceptor = new DynamicDataSourceAnnotationInterceptor(true, dsProcessor);
        DynamicDataSourceAdvisor advisor = new DynamicDataSourceAdvisor(interceptor, RestController.class, DS.class);
        return advisor;
    }

    @Role(2)
    @Bean
    @ConditionalOnProperty(
            prefix = "spring.datasource.dynamic",
            name = {"seata"},
            havingValue = "false",
            matchIfMissing = true
    )
    public Advisor dynamicTransactionAdvisor() {
        DynamicLocalTransactionInterceptor interceptor = new DynamicLocalTransactionInterceptor();
        return new DynamicDataSourceAnnotationAdvisor(interceptor, DSTransactional.class);
    }

    @Bean
    @ConditionalOnMissingBean
    public DataSourceInitEvent dataSourceInitEvent() {
        return new EncDataSourceInitEvent();
    }

    @Bean
    @ConditionalOnMissingBean
    public DsProcessor dsProcessor(BeanFactory beanFactory) {
        return (DsProcessor) ("DsPackageProcessor".equals(this.dsProcessor) ? new DsPackageProcessor() : new DsRestControllerProcessor());
    }

    @Override
    public void afterPropertiesSet() {
        if (!CollectionUtils.isEmpty(this.dataSourcePropertiesCustomizers)) {
            Iterator var1 = this.dataSourcePropertiesCustomizers.iterator();

            while (var1.hasNext()) {
                DynamicDataSourcePropertiesCustomizer customizer = (DynamicDataSourcePropertiesCustomizer) var1.next();
                customizer.customize(this.properties);
            }
        }

    }

    @Order(-2147483648)
    @Bean
    @ConditionalOnProperty(
            prefix = "spring.datasource.dynamic.extends.msdbInterceptor",
            name = {"enabled"},
            havingValue = "true",
            matchIfMissing = true
    )
    public MsdbInterceptor msdbInterceptor() {
        return new MsdbInterceptor();
    }

    @Bean
    @ConditionalOnProperty(
            prefix = "spring.datasource.dynamic.extends.readdbbypath",
            name = {"enabled"},
            havingValue = "true",
            matchIfMissing = false
    )
    public ICustomizeRoutingDataSource customizeRoutingDataSource() {
        return new DefaultRoutingDataSource();
    }
}
